/**
测试结果：

===============
在我的机器上: AMD Sempron(tm) Processor 3600+ 2GHz  1.4G memory
---
未优化的编译参数：

kb_archive : { count : 17718750; duration : 78113157; mips : 0.226834}
kb_archive_debug : { count : 17437500; duration : 81691266; mips : 0.213456}
xsocket_archive : { count : 30937500; duration : 237412508; mips : 0.130311}

kb_archive : { count : 4465116; duration : 19822156; mips : 0.225259}
kb_archive_debug : { count : 4645161; duration : 20715439; mips : 0.224237}
xsocket_archive : { count : 2769230; duration : 20332277; mips : 0.136199}

-O2级别优化：

kb_archive : { count : 31135135; duration : 14257053; mips : 2.18384}
kb_archive_debug : { count : 36000000; duration : 16409680; mips : 2.19383}
xsocket_archive : { count : 4663967; duration : 19823435; mips : 0.235275}

kb_archive : { count : 30315789; duration : 13834767; mips : 2.19128}
kb_archive_debug : { count : 37161290; duration : 17149654; mips : 2.16688}
xsocket_archive : { count : 4626506; duration : 19744483; mips : 0.234319}

结论：
1、kb::archive的两个实现性能差别很小
2、kb::archive的性能比xsocket的好1.5～9倍左右
3、加入编译器优化后，kb::archive的性能提升近10倍，xsocket的性能提升近1倍

 */
#include <sstream>
#include <iostream>
using namespace std;

#include <xbase_utility.hpp>
using namespace xbase;

namespace kb
{

	namespace performance_test
	{

		template<typename T>
		struct case_concept
		{
			typedef T case_type;

			// T::run();
			// T::name();
		};

		struct result
		{
			size_t m_count;
			size_t m_duration; // in us
			double m_mips;	// 10^6 count per second or count per
			// micro second

			void dump() {
				cout << "{ count : " << m_count
				     << "; duration : " << m_duration
				     << "; mips : " << m_mips
				     << "}";
			}
		};

		template<typename T>
		void run(T c, result *r)
		{
			const size_t init_count = 128;
			const uint64_t start = cur_micro_seconds();
			for(size_t i=0; i<128; ++i)
			{
				c.run();
			}
			const uint64_t end = cur_micro_seconds();
			const size_t duration = end - start + 1; // ensure duration >= 1
			if(duration >= 3 * 1000 * 1000) // >= 3s
			{
				r->m_count = 128;
				r->m_duration = duration;
				r->m_mips = static_cast<double>(init_count) / duration;
			}
			else
			{
				const size_t count = init_count * 1000 * 1000 * 9 / duration; // count for 9s
				const uint64_t start = cur_micro_seconds();
				for(size_t i=0; i<count; ++i)
				{
					c.run();
				}
				const uint64_t end = cur_micro_seconds();
				const size_t duration = end - start;
				const double mips = static_cast<double>(count) / duration;
				r->m_count = count;
				r->m_duration = duration;
				r->m_mips = mips;
			}
		}

		template<typename T1, typename T2>
		void run(T1 case1, T2 case2);

		template<typename T1, typename T2, typename T3>
		void run(T1 case1, T2 case2, T3 case3)
		{
			result r1, r2, r3;

			run(case1, &r1);
			cout << case1.name() << " : ";
			r1.dump();
			cout << endl;

			run(case2, &r2);
			cout << case2.name() << " : ";
			r2.dump();
			cout << endl;

			run(case3, &r3);
			cout << case3.name() << " : ";
			r3.dump();
			cout << endl;
		}

	}

}



// 分为DEBUG模式和NDEBUG模式，用-DNDEBUG来区分
#include <binary_iarchive.hpp>
#include <binary_oarchive.hpp>						\

typedef kb::reversed_byteorder<kb::host_byteorder>::type reverse_byteorder; // 确保有字节转换操作

typedef kb::binary_oarchive<reverse_byteorder,
			    kb::details::binary_archive_config_debug> binary_oarchive_debug;
typedef kb::binary_iarchive<reverse_byteorder,
			    kb::details::binary_archive_config_debug> binary_iarchive_debug;

typedef kb::binary_oarchive<reverse_byteorder,
			    kb::details::binary_archive_config_default> binary_oarchive;
typedef kb::binary_iarchive<reverse_byteorder,
			    kb::details::binary_archive_config_default> binary_iarchive;

#include <xsocket/bytebuffer.hpp>
#include <xsocket/biniarchive.hpp>
#include <xsocket/binoarchive.hpp>

// helper function for xsocket lib

template <typename Element>
BinIArchive &operator>> (BinIArchive &in, std::vector<Element> &vec) {
	uint32_t size = 0;
	in >> size;
	
	vec.clear ();
	// vec.capacity (size)
	Element elem;
	for (uint32_t i=0; i<size; ++i) {
		in >> elem;
		vec.push_back (elem);
	}
	
	return in;
}

template <typename Element>
BinOArchive &operator<< (BinOArchive &out, std::vector<Element> &vec) {
	uint32_t size = vec.size ();
	out << size;
	for (typename std::vector<Element>::iterator it=vec.begin ();
	     it != vec.end (); ++it) {
		out << (*it);
	}
	
	return out;
}

class packet
{
public:
	uint32_t program_version;
	std::string tablet_name;
	std::string start_row_name;
	std::string end_row_name;
	std::vector<std::string> column_names;
	uint64_t latest_timestamp;
	uint64_t oldest_timestamp;
	uint32_t ttl;
	uint32_t version_number;
	uint32_t options;
	uint32_t num_row;

	template<typename Archive>
	inline
	bool load(Archive &ar) {
		ar >> program_version;
		ar >> tablet_name;
		ar >> start_row_name;
		ar >> end_row_name;
		ar >> column_names;
		ar >> latest_timestamp;
		ar >> oldest_timestamp;
		ar >> ttl;
		ar >> version_number;
		ar >> options;
		ar >> num_row;
		return ar.good();
	}

	template<typename Archive>
	inline
	bool save(Archive &ar) const {
		ar << program_version;
		ar << tablet_name;
		ar << start_row_name;
		ar << end_row_name;
		ar << column_names;
		ar << latest_timestamp;
		ar << oldest_timestamp;
		ar << ttl;
		ar << version_number;
		ar << options;
		ar << num_row;
		return ar.good();
	}

	// interface for xsocket lib
	template<typename Archive>
	inline
	void serialize(Archive &ar) {
		ar & program_version;
		ar & tablet_name;
		ar & start_row_name;
		ar & end_row_name;
		ar & column_names;
		ar & latest_timestamp;
		ar & oldest_timestamp;
		ar & ttl;
		ar & version_number;
		ar & options;
		ar & num_row;
	}
};

packet p;
void init_packet()
{
	p.program_version = 1;
	p.tablet_name = "this is p.tablet_name";
	p.start_row_name = "this is p.start_row_name";
	p.end_row_name = "this is p.end_row_name";
	for(int i=0; i<1000; ++i)
	{
		ostringstream out;
		out << "this is number " << i;
		p.column_names.push_back(out.str());
	}
	p.latest_timestamp = 2;
	p.oldest_timestamp = 3;
	p.ttl = 4;
	p.version_number = 5;
	p.options = 6;
	p.num_row = 7;
}

const size_t size = 1024 * 1024;
char buffer[size];
const ByteBuffer bb(-1, 0, size, size, buffer, 0);

struct case_xsocket_archive
{
	void run() {
		ByteBuffer bb_copy = bb;
		BinOArchive out(bb_copy);
		out & p;
		bb_copy.flip();
		BinIArchive in(bb_copy);
		in & p;
	}

	const char *name() const {
		return "xsocket_archive";
	}
};

template<typename iarchive, typename oarchive>
struct case_kb_archive_base
{
	void run() {
		oarchive out(buffer, size);
		out << p;
		iarchive in(buffer, size);
		in >> p;
	}
};

struct case_kb_archive
	: public case_kb_archive_base<binary_iarchive,
				      binary_oarchive>
{
	using case_kb_archive_base<binary_iarchive,
				   binary_oarchive>::run;

	const char *name() const {
		return "kb_archive";
	}
};

struct case_kb_archive_debug
	: public case_kb_archive_base<binary_iarchive_debug,
				      binary_oarchive_debug>
{
	using case_kb_archive_base<binary_iarchive_debug,
				   binary_oarchive_debug>::run;

	const char *name() const {
		return "kb_archive_debug";
	}
};

int main(int argc, char **argv)
{
	case_kb_archive case_ka;
	case_kb_archive_debug case_kad;
	case_xsocket_archive case_xa;

	kb::performance_test::run(case_ka, case_kad, case_xa);

	return 0;
}
